package cn.bellychang.mercyblitz_demo;

import cn.bellychang.response.ErrorCode;
import cn.bellychang.response.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author ChangLiang
 * @date 2020/10/17
 */
@RestController
@EnableScheduling
public class DeferredResultAsyncController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 执行是同一个线程
     *
     * @return
     */
    @GetMapping("/test-deferred-result")
    public DeferredResult<Response> testDeferredResult() throws InterruptedException {
        DeferredResult<Response> result = new DeferredResult<>(200L);

        logger.info("request start...");
        TimeUnit.MILLISECONDS.sleep(100);

        Response response = new Response();
        response.setErrorCode(ErrorCode.OK.code);
        response.setValue(ErrorCode.OK.value);
        result.setResult(response);
        result.onCompletion(() -> logger.info("执行结束"));
        result.onTimeout(() -> logger.warn("执行超时"));
        result.onError((throwable) -> logger.error("执行出错 throwable:{}", throwable.getMessage()));
        return result;
    }

    /**
     * 当不设置超时时间，其也是会超时的，并且会进行线程的切换
     *
     * @return
     */
    @GetMapping("/test-deferred-result2")
    public DeferredResult<Response> testDeferredResult2() {
        // 当我们不设置超时时间，那么这个超时时间就是tomcat内部就有的一个异步超时时间
        DeferredResult<Response> result = new DeferredResult<>();

        logger.info("request start...");

        Response response = new Response();
        response.setErrorCode(ErrorCode.OK.code);
        response.setValue(ErrorCode.OK.value);
//        result.setResult(response);
        result.onCompletion(() -> logger.info("执行结束"));
        result.onTimeout(() -> logger.warn("执行超时"));
        result.onError((throwable) -> logger.error("执行出错 throwable:{}", throwable.getMessage()));
        return result;
    }

    @GetMapping("/test-deferred-result3")
    public DeferredResult<Response> testDeferredResult3() {
        // 50ms
        DeferredResult<Response> result = new DeferredResult<>(50L);

        logger.info("request start...");

        Response response = new Response();
        response.setErrorCode(ErrorCode.OK.code);
        response.setValue(ErrorCode.OK.value);
//        result.setResult(response);
        result.onCompletion(() -> logger.info("执行结束"));
        result.onTimeout(() -> logger.warn("执行超时"));
        result.onError((throwable) -> logger.error("执行出错 throwable:{}", throwable.getMessage()));
        return result;
    }

    @GetMapping("/test-deferred-result4")
    public DeferredResult<Response> testDeferredResult4() {
        // 这种方式无法进行请求参数的传递 仅能作为demo
        // 需要进行参数传递 需要借助一个中间层 如DeferredResultHolder#Map
        DeferredResult<Response> result = new DeferredResult<>(2000L);
        // offer: if queue is full, rather than throws exception, but return false
        // add: if queue is full, throws exception
        // put: if queue is full, will wait
        queue.offer(result);

        logger.info("request start...");

//        result.setResult(response);
        result.onCompletion(() -> logger.info("执行结束"));
        result.onTimeout(() -> logger.warn("执行超时"));
        result.onError((throwable) -> logger.error("执行出错 throwable:{}", throwable.getMessage()));
        return result;
    }

    private final BlockingQueue<DeferredResult<Response>> queue = new ArrayBlockingQueue<>(5);

    final Random random = new Random();

    /**
     * 正常是每5秒执行一次此方法
     */
    @Scheduled(fixedRate = 5000)
    public void process() throws InterruptedException {

        DeferredResult<Response> result;
        do {
            // 正常是每5秒执行一次此方法
            // 但是由于其result如果为空 就会阻塞在这里 从而其永不为空
            // 即启动一次就会while(true)执行 而spring @Scheduled是固定一个线程执行的 这个方法未执行完成 其会进行等待？ 所以这个线程永远在这里执行了
            // 这就是通过@Scheduled启动一个while(true)线程的方法
            result = queue.take();
            // 随机超时时间
            // 模拟等待时间，RPC 或者 DB 查询
            int timeout = 1000 + random.nextInt(2000);
            TimeUnit.MILLISECONDS.sleep(timeout);
            // 计算结果
            Response response = new Response();
            response.setErrorCode(ErrorCode.OK.code);
            response.setValue(ErrorCode.OK.value);
            result.setResult(response);
            logger.info("执行计算结果，消耗：{} ms", timeout);
        } while (result != null);
    }
}
